package com.my.db.test.mybatis.base;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.my.core.domain.exception.ServiceException;
import com.my.core.page.PageInfo;
import com.my.core.page.PageParam;
import com.my.core.util.ClassUtil;
import com.my.core.util.spring.BeanUtil;
import com.my.db.test.mybatis.annotation.Unique;
import lombok.SneakyThrows;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author timo 2021/3/12
 */
@Validated
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements BaseService<T> {
    @Override
    public TableInfo getTable() {
        return SqlHelper.table(entityClass);
    }

    @Override
    public boolean deleteById(Serializable id) {
        return  super.removeById(id);
    }

    /**
     * 单表分页查询
     * @param pageParam
     * @param entity
     * @return
     */
    public PageInfo<T> selectPage(PageParam pageParam, T entity) {
        return this.selectPage(pageParam, Wrappers.query(entity));
    }

    /**
     * 条件列表分页查询
     * @param pageParam
     * @param queryWrapper
     * @return
     */
    public PageInfo<T> selectPage(PageParam pageParam, Wrapper<T> queryWrapper) {
        PageInfo<T> pageInfo = new PageInfo();
        Integer pageNumber = pageParam.getPageNumber();
        Integer pageSize = pageParam.getPageSize();
        if (com.baomidou.mybatisplus.core.toolkit.ObjectUtils.isEmpty(pageNumber) && com.baomidou.mybatisplus.core.toolkit.ObjectUtils.isEmpty(pageSize)) {
            List<T> list = baseMapper.selectList(queryWrapper);
            pageInfo.setDataList(list);
            pageInfo.setTotal(com.baomidou.mybatisplus.core.toolkit.ObjectUtils.isEmpty(list) ? 0 : list.size());
        } else {
            Page<T> page = new Page<>(pageNumber, pageSize);
            Page<T> listPage = super.page(page, queryWrapper);
            pageInfo.setTotal(listPage.getTotal());
            pageInfo.setDataList(page.getRecords());
        }
        return pageInfo;
    }

    public <V> PageInfo<V> selectPage(Page<V> page) {
        PageInfo<V> pageInfo = new PageInfo();
        pageInfo.setTotal(page.getTotal());
        pageInfo.setDataList(page.getRecords());
        return pageInfo;
    }

    @Override
    public boolean save(T entity) {
        this.resolveEntity(entity);
        return super.save(entity);
    }
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveBatch(entityList, batchSize);
    }
    @Override
    public boolean updateById(T entity) {
        this.resolveEntity(entity);
        return super.updateById(entity);
    }
    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.updateBatchById(entityList, batchSize);
    }
    @Override
    public boolean saveOrUpdate(T entity) {
        return entity.getId() == null ? this.save(entity) : this.updateById(entity);
    }
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        entityList.forEach(this::resolveEntity);
        return super.saveOrUpdateBatch(entityList, batchSize);
    }
    @Override
    @Transactional(
            rollbackFor = {Exception.class}
    )
    public boolean deleteLogic(@NotEmpty List<Long> ids) {
//        BladeUser user = AuthUtil.getUser();
        List<T> list = new ArrayList();
        ids.forEach((id) -> {
            T entity = (T) BeanUtil.newInstance(this.currentModelClass());
//            if (user != null) {
//                entity.setUpdateUser(user.getUserId());
//            }

            entity.setUpdateTime(DateUtil.date());
            entity.setId(id);
            list.add(entity);
        });
        return super.updateBatchById(list) && super.removeByIds(ids);
    }

    @Override
    public boolean changeStatus(@NotEmpty List<Long> ids, Integer status) {
//        BladeUser user = AuthUtil.getUser();
        List<T> list = new ArrayList();
        ids.forEach((id) -> {
            T entity = (T) BeanUtil.newInstance(this.currentModelClass());
//            if (user != null) {
//                entity.setUpdateUser(user.getUserId());
//            }

            entity.setUpdateTime(DateUtil.date());
            entity.setId(id);
            entity.setStatus(status);
            list.add(entity);
        });
        return super.updateBatchById(list);
    }

    @SneakyThrows
    private void resolveEntity(T entity) {
        try {
            BaseEntity baseEntity = (BaseEntity) entity;
            Map uniqueFieldMap = this.getUniqueField(entity);
            BaseEntity result = null;
            if (uniqueFieldMap.size() > 0) {
                QueryWrapper queryWrapper = Wrappers.<T>query()
                        .select("id")
                        .allEq(uniqueFieldMap);
                result = (BaseEntity) super.getOne(queryWrapper);
            }
            if (ObjectUtils.isNotEmpty(result)) {
                if (baseEntity.getId() != null) {
                    // 修改的数据id 不一样，存在相同数据
                    if (baseEntity.getId().intValue() != result.getId().intValue()) {
                        throw new ServiceException("该数据已存在,请勿重复添加");
                    }
                } else {
                    throw new ServiceException("该数据已存在,请勿重复添加");
                }
            }

//            BladeUser user = AuthUtil.getUser();
            Date now = DateUtil.date();
            if (entity.getId() == null) {
//                if (user != null) {
//                    entity.setCreateUser(user.getUserId());
//                    entity.setCreateDept(Func.firstLong(user.getDeptId()));
//                    entity.setUpdateUser(user.getUserId());
//                }

                if (entity.getStatus() == null) {
                    entity.setStatus(1);
                }

                entity.setCreateTime(now);
            }
//            else if (user != null) {
//                entity.setUpdateUser(user.getUserId());
//            }

            entity.setUpdateTime(now);
            entity.setIsDeleted(0);
            Field field = ReflectUtil.getField(entity.getClass(), "tenantId");
            if (ObjectUtil.isNotEmpty(field)) {
                Method getTenantId = ClassUtil.getMethod(entity.getClass(), "getTenantId", new Class[0]);
                String tenantId = String.valueOf(getTenantId.invoke(entity));
                if (ObjectUtil.isEmpty(tenantId)) {
                    Method setTenantId = ClassUtil.getMethod(entity.getClass(), "setTenantId", new Class[]{String.class});
                    setTenantId.invoke(entity, null);
                }
            }

        } catch (Throwable var8) {
            throw var8;
        }
    }

    /**
     * 获取需要进行唯一约束校验的字段
     * @return
     */
    private Map<String, Object> getUniqueField(T entity) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        Map uniqueField = new HashMap<>();
        fieldList.forEach(tableFieldInfo -> {
            Field field = tableFieldInfo.getField();
            Unique unique = field.getAnnotation(Unique.class);
            if (ObjectUtils.isNotEmpty(unique)) {
                try {
                    field.setAccessible(true);
                    String column = ObjectUtils.isEmpty(unique.value()) ? field.getName() : unique.value();
                    uniqueField.put(column, field.get(entity));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        return uniqueField;
    }
}
